package jef.database.innerpool;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import jef.database.DbUtils;
import jef.database.innerpool.PoolService.CheckableConnection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 最简单的IConnection实现
 * @author jiyi
 *
 */
final class SingleConnection implements CheckableConnection,ReentrantConnection{
	private static Logger log=LoggerFactory.getLogger(SingleConnection.class);
	/**
	 * Belongs to the connection pool.
	 */
	private IPool<?> parent;
	/**
	 * the jdbc connection
	 */
	private Connection conn;
	/**
	 * The connection was locked by the object.
	 */
	private volatile Object used;
	/**
	 * Reentrant count of the lock. 
	 */
	private volatile int count;
	
	SingleConnection(Connection connection,
			IPool<?> parent) {
		this.conn=connection;
		this.parent=parent;
	}

	public void closePhysical(){
		if(conn!=null){
			DbUtils.closeConnection(conn);
			conn = null;
		}
	}

	public void ensureOpen() throws SQLException {
		if(conn==null || conn.isClosed()){
			long start=System.currentTimeMillis();
			DataSource ds=parent.getDatasource();
			conn=ds.getConnection();
			log.info("Create connection to {}, cost {}ms.",ds,System.currentTimeMillis()-start);
		}
	}

	public void setKey(String key) {
	}

	public void commit() throws SQLException {
		conn.commit();
	}


	public Statement createStatement(int resultSetType, int resultSetConcurrency)
			throws SQLException {
		return conn.createStatement(resultSetType, resultSetConcurrency);
	}

	public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
			throws SQLException {
		return conn.prepareStatement(sql, autoGeneratedKeys);
	}

	public PreparedStatement prepareStatement(String sql, int resultSetType,
			int resultSetConcurrency) throws SQLException {
		return conn.prepareStatement(sql, resultSetType, resultSetConcurrency);
	}

	public PreparedStatement prepareStatement(String sql, String[] columnNames)
			throws SQLException {
		return conn.prepareStatement(sql, columnNames);
	}

	public Statement createStatement() throws SQLException {
		return conn.createStatement();
	}

	public PreparedStatement prepareStatement(String sql) throws SQLException {
		return conn.prepareStatement(sql);
	}

	public CallableStatement prepareCall(String sql) throws SQLException {
		return conn.prepareCall(sql);
	}

	public void setAutoCommit(boolean b) throws SQLException {
		if(conn.getAutoCommit()!=b){
			conn.setAutoCommit(b);
		}
	}

	public DatabaseMetaData getMetaData() throws SQLException {
		return conn.getMetaData();
	}

	public void rollback() throws SQLException {
		conn.rollback();
	}

	public Savepoints setSavepoints(String savepointName) throws SQLException {
		return new Savepoints(conn,conn.setSavepoint(savepointName));
	}
	
	public void setUsedByObject(Object user) {
		this.used=user;
		count++;
	}
	
	public Object popUsedByObject() {
		if(--count>0){ 
//			log.debug("not return connection {} counter:{}.",used,count);
			return null;
		}else{
			Object o=used;
			used=null;
			return o;
		}
	}

	public void addUsedByObject() {
		count++;
	}

	public boolean isUsed() {
		return count>0;
	}

	public void setInvalid() {
		DbUtils.closeConnection(conn);
		conn = null;
	}

	public boolean checkValid(String testSql) throws SQLException {
		if(conn==null)return true;
		PreparedStatement st=conn.prepareStatement(testSql);
		try{
			st.execute();
			return true;
		}finally{
			DbUtils.close(st);
		}
	}
	
	public boolean checkValid(int timeout) throws SQLException {
		if(conn==null)return true;
		return conn.isValid(timeout);
	}

	public void notifyDisconnect() {
	}

	public void setReadOnly(boolean readOnly) throws SQLException {
		conn.setReadOnly(readOnly);
	}
	
	public int getTransactionIsolation() throws SQLException{
		return conn.getTransactionIsolation();
	}
	
	public void setTransactionIsolation(int level) throws SQLException{
		conn.setTransactionIsolation(level);
	}
}
